using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using Crownwood.Magic.Docking;
using Crownwood.Magic.Common;

using Jeffrey.XFramework;
using Jeffrey.XFramework.XInteract;
using Jeffrey.XFramework.XCrawl;

namespace Jeffrey.Xuthus
{
    public partial class XMainForm : Form
    {
        private static readonly String s_CombBoxTooltips = "use semicolon to separate the urls";

        public XMainForm(XController xc, CrawlController cc)
        {
            m_XController = xc;
            m_CrawlController = cc;

            m_ProcessorsForm = new XProcessorsForm(xc, cc);
            m_SettingsForm   = new XSettingsForm(xc, cc);

            InitializeComponent();

            //inital
            #region Docking Settings...

            m_XDockManager = new DockingManager(this, Crownwood.Magic.Common.VisualStyle.IDE);
            m_XDockManager.InnerControl = XTableLayoutPanel;
            m_XDockManager.OuterControl = XStatusStrip;

            m_XProcessorsFormContent = new Content(m_XDockManager);
            m_XProcessorsFormContent.Control = m_ProcessorsForm;
            Size XProcessorsFormSize = m_XProcessorsFormContent.Control.Size;
            m_XProcessorsFormContent.Title = "XProcessors";
            m_XProcessorsFormContent.FullTitle = "XProcessors Config";
            m_XProcessorsFormContent.AutoHideSize = XProcessorsFormSize;
            m_XProcessorsFormContent.DisplaySize = XProcessorsFormSize;

            m_XSettingsFormContent = new Content(m_XDockManager);
            m_XSettingsFormContent.Control = m_SettingsForm;
            Size XSettingsFormSize = m_XSettingsFormContent.Control.Size;
            m_XSettingsFormContent.Title = "Settings";
            m_XSettingsFormContent.FullTitle = "XSpiders Settings";
            m_XSettingsFormContent.AutoHideSize = XSettingsFormSize;
            m_XSettingsFormContent.DisplaySize = XSettingsFormSize;

            m_XDockManager.Contents.Add(m_XProcessorsFormContent);
            m_XDockManager.Contents.Add(m_XSettingsFormContent);

            WindowContent XWC = m_XDockManager.AddContentWithState(m_XProcessorsFormContent, State.DockRight);
            XWC.Font = new System.Drawing.Font("Nina", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            m_XDockManager.AddContentToWindowContent(m_XSettingsFormContent, XWC);

            m_XDockManager.ContentHidden += XDockManager_ContentHidden;
            m_XDockManager.ContentShown  += XDockManager_ContentShown;

            #endregion

            m_Timer = new System.Timers.Timer(2 * 1000);
        }

        public void InitializeStuffs()
        {
            //register the event observation...
            m_CrawlController.Xbuject.SpiderStatusChanged += OnSpdierStatusChanged;
            m_CrawlController.Xbuject.DownLoading += OnDownLoading;
            m_CrawlController.Xbuject.ErrorOccurred += OnErrors;
            m_CrawlController.Xbuject.AllSpdiersStop += OnAllStops;

            CpuStatusLabel.Alignment    = ToolStripItemAlignment.Right;
            MemoryStatusLabel.Alignment = ToolStripItemAlignment.Right;

            InternetStatusLabel.Text = "Internet: Checking...";

            m_Timer.AutoReset = true;
            m_Timer.Enabled   = true;
            m_Timer.Elapsed  += ShowStatusStripContent;

            CpuStatusLabel.Image = CpuLoadImageList.Images[0];
            InternetStatusLabel.Image  = NetImageList.Images[0];

            XTrackBar.Value = m_CrawlController.ShouldRunning;
            XThreadNumLabel.Text = "Threads: " + XTrackBar.Value;

            SetSourceLinkComboBox();

            for (Int32 i = 0; i < m_CrawlController.MaxThreadNum; i++)
            {
                ListViewItem item = XThreadsListView.Items.Add((i + 1).ToString());
                item.SubItems.AddRange(new String[] { String.Empty,
                                                      "Prepared",
                                                      String.Empty,
                                                      String.Empty,
                                                      String.Empty,
                                                      String.Empty,
                                                     }
                                      );
            }

            XToolStrip.ImageList = ToolStripImageList;
            StartToolStripButton.Image = ToolStripImageList.Images[0];
            StopToolStripButton.Image  = ToolStripImageList.Images[1];
            PauseToolStripButton.Image = ToolStripImageList.Images[2];

            SchemeDropDownButton.Image = ToolStripImageList.Images[5];

            LoadConfigToolStripButton.Image = ToolStripImageList.Images[6];
            SaveConfigToolStripButton.Image = ToolStripImageList.Images[7];

            PauseToolStripButton.Enabled = false;
            pauseToolStripMenuItem.Enabled = false;

            StopToolStripButton.Enabled  = false;
            stopToolStripMenuItem.Enabled = false;

            m_ProcessorsForm.InitializeStuffs();
            m_SettingsForm.InitializeStuffs();
        }

        public void ShowErrorDetails(String error)
        {
            XErrorsTextBox.ForeColor = Color.Red;
            XErrorsTextBox.Text = @"" + error + @""; 
        }

        public XProcessorsForm ProcessorsForm { get { return m_ProcessorsForm; } }
        public XSettingsForm SettingsForm { get { return m_SettingsForm; } }

        //Show the spider status on the ListView...
        private void OnSpdierStatusChanged(Object sender, SpiderStatusPackage ssp)
        {
            //ignore this status for now...
            if (ssp.Status == CrawlSpider.SpiderStatus.FinishedProcessor)
                return;

            if (XThreadsListView.InvokeRequired)
            {
                XThreadsListView.BeginInvoke(new SpdiersEventHandler<SpiderStatusPackage>(OnSpdierStatusChanged), new Object[] { sender, ssp });
            }
            else
            {
                ListViewItem item = XThreadsListView.Items[ssp.ThreadNum];

                if (ssp.ThreadNum >= m_CrawlController.ShouldRunning)
                {
                    item.ForeColor = Color.Black;
                    item.SubItems[1].Text = String.Empty;
                    item.SubItems[2].Text = "Shutting Down...waiting";
                    item.SubItems[3].Text = String.Empty;
                    item.SubItems[4].Text = String.Empty;
                    item.SubItems[5].Text = String.Empty;
                    item.SubItems[6].Text = String.Empty;

                    if (ssp.Status != CrawlSpider.SpiderStatus.ShutDown)
                        return;
                }

                switch (ssp.Status)
                {
                    case CrawlSpider.SpiderStatus.Start:
                        {
                            item.ForeColor = Color.Black;
                            item.SubItems[1].Text = String.Empty;
                            item.SubItems[2].Text = "Spider Starting";
                            item.SubItems[3].Text = String.Empty;
                            item.SubItems[4].Text = String.Empty;
                            item.SubItems[5].Text = String.Empty;
                            item.SubItems[6].Text = String.Empty;
                        } break;
                    case CrawlSpider.SpiderStatus.GetNewUri:
                        {
                            item.SubItems[1].Text = String.Empty;
                            item.SubItems[2].Text = "Begin";
                            item.SubItems[3].Text = ssp.Depth;
                            item.SubItems[4].Text = ssp.Uri;
                        } break;
                    case CrawlSpider.SpiderStatus.EntryingProcessor:
                        {
                            item.SubItems[1].Text = ssp.ProcessorName;
                            item.SubItems[2].Text = "Entrying";
                        } break;
                    case CrawlSpider.SpiderStatus.Connecting:
                        {
                            item.ForeColor = Color.Red;
                            item.SubItems[2].Text = "Connecting";
                        } break;
                    case CrawlSpider.SpiderStatus.ReceivingHeader:
                        {
                            item.ForeColor = Color.Black;
                            item.SubItems[2].Text = "ReceivingHeader";
                        } break;
                    case CrawlSpider.SpiderStatus.Downloading:
                        {
                            item.ForeColor = Color.Green;
                            item.SubItems[2].Text = "Downloading";
                        } break;
                    case CrawlSpider.SpiderStatus.Downloaded:
                        {
                            item.ForeColor = Color.Green;
                            item.SubItems[2].Text = "Downloaded";
                            item.SubItems[5].Text = String.Empty;
                            item.SubItems[6].Text = String.Empty;
                        } break;
                    case CrawlSpider.SpiderStatus.FinishedUri:
                        {
                            item.ForeColor = Color.Black;
                            item.SubItems[1].Text = String.Empty;
                            item.SubItems[2].Text = "Finshed";
                        } break;
                    case CrawlSpider.SpiderStatus.Waiting:
                        {
                            item.ForeColor = Color.Black;
                            item.SubItems[1].Text = String.Empty;
                            item.SubItems[2].Text = "Waiting...";
                            item.SubItems[3].Text = String.Empty;
                            item.SubItems[4].Text = String.Empty;
                            item.SubItems[5].Text = String.Empty;
                            item.SubItems[6].Text = String.Empty;
                        } break;
                    case CrawlSpider.SpiderStatus.ShutDown:
                        {
                            item.ForeColor = Color.Black;
                            item.SubItems[1].Text = String.Empty;
                            item.SubItems[2].Text = "Prepared";
                            item.SubItems[3].Text = String.Empty;
                            item.SubItems[4].Text = String.Empty;
                            item.SubItems[5].Text = String.Empty;
                            item.SubItems[6].Text = String.Empty;
                        } break;
                    default: return;
                }
            }
        }

        private void OnDownLoading(Object sender, DownLoadingPackage dlp)
        {
            if (XThreadsListView.InvokeRequired)
            {
                XThreadsListView.BeginInvoke(new SpdiersEventHandler<DownLoadingPackage>(OnDownLoading), new Object[] { sender, dlp });
            }
            else
            {
                ListViewItem item = XThreadsListView.Items[dlp.ThreadNum];

                if (dlp.ThreadNum >= m_CrawlController.ShouldRunning)
                {
                    item.ForeColor = Color.Black;
                    item.SubItems[1].Text = String.Empty;
                    item.SubItems[2].Text = "Shutting Down...waiting";
                    item.SubItems[3].Text = String.Empty;
                    item.SubItems[4].Text = String.Empty;
                    item.SubItems[5].Text = String.Empty;
                    item.SubItems[6].Text = String.Empty;
                    return;
                }

                item.SubItems[5].Text = CrawlToolKits.Commas(dlp.Bytes);

                switch (dlp.Total)
                {
                    case 0:
                        {
                            if (!(item.SubItems[6].Text == "N/A"))
                                item.SubItems[6].Text = "N/A";
                        } break;
                    case -1:
                        {
                            if (!(item.SubItems[6].Text == "Chunked"))
                                item.SubItems[6].Text = "Chunked";
                        } break;
                    default:
                        {
                            Double pct = (((Double)dlp.Bytes) / dlp.Total) * 100;
                            item.SubItems[6].Text = pct.ToString("0.00") + "%";
                        } break;
                }
            }
        }

        private void OnErrors(Object sender, ErrorPackage ep)
        {
            if (XErrorsListView.InvokeRequired)
            {
                XErrorsListView.BeginInvoke(new SpdiersEventHandler<ErrorPackage>(OnErrors), new Object[] { sender, ep });
            }
            else
            {
                if (m_XController.LastNErrors > 0)
                {
                    ErrorSequence++;

                    if (m_XController.LastNErrors == XErrorsListView.Items.Count)
                        XErrorsListView.Items.RemoveAt(0);

                    ListViewItem item = XErrorsListView.Items.Add("Error " + ErrorSequence.ToString());
                    item.SubItems.AddRange(new String[] { ep.Date, ep.Uri });

                    if(ep.Detail != String.Empty)
                        m_XController.LogErrors(ErrorSequence, ep.Error + ": \r\n" + ep.Detail);
                    else
                        m_XController.LogErrors(ErrorSequence, ep.Error + ".");
                }
            }
        }

        private void GoButton_Click(Object sender, EventArgs e)
        {
            String[] urls;
            if (GetUrlsFromComboBox(out urls))
            {
                m_XController.RunSpdiers(urls);
                XGoButton.Enabled = false;
            }
        }

        private Boolean GetUrlsFromComboBox(out String[] urls)
        {
            urls = null;

            String sourcelinks = XSourceLinkComboBox.Text;
            if (sourcelinks == String.Empty || sourcelinks == s_CombBoxTooltips)
                return false;

            sourcelinks = sourcelinks.Trim();

            Regex reg = new Regex(Extractor.UriValidatePattern, RegexOptions.IgnoreCase);
            urls = sourcelinks.Split(new Char[] { ' ' });

            for (Int32 i = 0; i < urls.Length; i++)
            {
                if (urls[i] == String.Empty)
                    continue;

                urls[i] = urls[i].Trim();
                Match m = reg.Match(urls[i]);
                if (!m.Success)
                {
                    MessageBox.Show("Urls' Formation Error.\r\n\r\n(Tips: using whitespace to separate the urls)",
                                     "Formation Error",
                                     MessageBoxButtons.OK,
                                     MessageBoxIcon.Error
                                    );
                    return false;
                }
            }

            return true;
        }

        private void XMainForm_FormClosing(Object sender, FormClosingEventArgs e)
        {
            if (DialogResult.Yes == MessageBox.Show("Are you sure to quit?",
                                                    "Quit",
                                                    MessageBoxButtons.YesNo,
                                                    MessageBoxIcon.Question
                                                   )
               )
            {
                if (bPaused)
                    PauseToolStripButton_Click(this, new EventArgs());
                m_XController.Stop();
            }
            else
            {
                e.Cancel = true;
            }

        }

        private void XErrorsListView_SelectedIndexChanged(Object sender, EventArgs e)
        {
            if (XErrorsListView.SelectedItems.Count > 0)
            {
                ListViewItem item = XErrorsListView.SelectedItems[0];
                m_XController.ShowErrorDetails(Int32.Parse(item.Text.Substring(6)));
            }
        }

        private void ShowStatusStripContent(Object sender, System.Timers.ElapsedEventArgs ee)
        {
            InternetStatusLabel.Text = m_XController.InternetState;
            MemoryStatusLabel.Text = "Memory: " + CrawlToolKits.FreeMemorySize + " MB Available";

            FinishedStatusLabel.Text = "Already Finished:  " + m_CrawlController.AlreadyFinished;

            Int32 usage = CrawlToolKits.CpuUsage;
            CpuStatusLabel.Image = CpuLoadImageList.Images[usage / 10];
            CpuStatusLabel.ToolTipText = "Cpu usage; " + usage + "%";
        }

        private void XTrackBar_MouseCaptureChanged(Object sender, EventArgs e)
        {
            m_CrawlController.ShouldRunning = XTrackBar.Value;
        }

        private void XTrackBar_ValueChanged(Object sender, EventArgs e)
        {
            XThreadNumLabel.Text = "Threads: " + XTrackBar.Value;
        }

        private void XSourceLinkComboBox_Enter(Object sender, EventArgs e)
        {
            if (XSourceLinkComboBox.Text == s_CombBoxTooltips)
            {
                XSourceLinkComboBox.Text = String.Empty;
            }
            XSourceLinkComboBox.ForeColor = Color.Black;
        }

        private void XSourceLinkComboBox_Leave(Object sender, EventArgs e)
        {
            if (XSourceLinkComboBox.Text == String.Empty)
            {
                XSourceLinkComboBox.ForeColor = Color.DarkGray;
                XSourceLinkComboBox.Text = s_CombBoxTooltips;
            }
        }

        private void XSourceLinkComboBox_KeyUp(Object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space &&
                e.Modifiers == Keys.None &&
                XSourceLinkComboBox.Focused &&
                XSourceLinkComboBox.Text.EndsWith(" ")
               )
            {
                XSourceLinkComboBox.Text += "http://";
                SendKeys.SendWait("{End}");
            }
            else if (e.KeyCode == Keys.Enter &&
                e.Modifiers == Keys.None &&
                XSourceLinkComboBox.Focused &&
                XGoButton.Enabled
               )
            {
                GoButton_Click(this, new EventArgs());
            }
        }

        private void SetSourceLinkComboBox()
        {
            String urls = m_XController.GetSourceLinks();
            if (urls == String.Empty)
            {
                XSourceLinkComboBox.ForeColor = Color.DarkGray;
                XSourceLinkComboBox.Text = s_CombBoxTooltips;
            }
            else
                XSourceLinkComboBox.Text = urls;
        }

        private void StartToolStripButton_MouseEnter(Object sender, EventArgs e)
        {
            StartToolStripButton.Image = ToolStripImageList.Images[3];
        }

        private void StartToolStripButton_MouseLeave(Object sender, EventArgs e)
        {
            StartToolStripButton.Image = ToolStripImageList.Images[0];
        }

        private void PauseToolStripButton_MouseLeave(Object sender, EventArgs e)
        {
            if(bPaused)
                PauseToolStripButton.Image = ToolStripImageList.Images[9];
            else
                PauseToolStripButton.Image = ToolStripImageList.Images[2];
        }

        private void PauseToolStripButton_MouseEnter(Object sender, EventArgs e)
        {
            if(bPaused)
                PauseToolStripButton.Image = ToolStripImageList.Images[8];
            else
                PauseToolStripButton.Image = ToolStripImageList.Images[4];
        }

        private void XGoButton_EnabledChanged(Object sender, EventArgs e)
        {
            if (XGoButton.Enabled)
            {
                PauseToolStripButton.Enabled   = false;
                pauseToolStripMenuItem.Enabled = false;

                StopToolStripButton.Enabled   = false;
                stopToolStripMenuItem.Enabled = false;

                StartToolStripButton.Enabled = true;
                runToolStripMenuItem.Enabled = true;

                LoadConfigToolStripButton.Enabled = true;
                reLaodToolStripMenuItem.Enabled   = true;
                SaveConfigToolStripButton.Enabled = true;
                saveToolStripMenuItem.Enabled     = true;

                m_ProcessorsForm.Enable();
                m_SettingsForm.Enable();
            }
            else
            {
                PauseToolStripButton.Enabled   = true;
                pauseToolStripMenuItem.Enabled = true;

                StopToolStripButton.Enabled   = true;
                stopToolStripMenuItem.Enabled = true;

                StartToolStripButton.Enabled = false;
                runToolStripMenuItem.Enabled = false;

                LoadConfigToolStripButton.Enabled = false;
                SaveConfigToolStripButton.Enabled = false;
                reLaodToolStripMenuItem.Enabled   = false;
                saveToolStripMenuItem.Enabled     = false;

                m_ProcessorsForm.Disable();
                m_SettingsForm.Disable();
            }
        }

        private void StartToolStripButton_Click(Object sender, EventArgs e)
        {
            GoButton_Click(sender, e);
        }

        private void StopToolStripButton_Click(Object sender, EventArgs e)
        {
            if (DialogResult.Yes == MessageBox.Show("Are you sure to stop all the spiders ?",
                                    "Stop Confirm",
                                    MessageBoxButtons.YesNo,
                                    MessageBoxIcon.Question
                                   )
               )
            {
                m_ProgressForm = new XProgressForm();
                //wait for the AllStopsEvent...
                m_CrawlController.AsyncCrawlStop();

                m_ProgressForm.ShowDialog();

                XGoButton.Enabled = true;
                //reset the number of spiders should running in next time
                m_CrawlController.ShouldRunning = XTrackBar.Value;
            }
        }

        private void OnAllStops(Object sender, EventArgs e)
        {
            if (m_ProgressForm != null)
            {
                m_ProgressForm.Close();
            }
        }

        private void PauseToolStripButton_Click(Object sender, EventArgs e)
        {
            if (bPaused)
            {
                m_XController.Continue();
                bPaused = false;
                StopToolStripButton.Enabled = true;
                stopToolStripMenuItem.Enabled = true;
                PauseToolStripButton.Text = "Pause";
                pauseToolStripMenuItem.Text = "Pause";
            }
            else
            {
                m_XController.Paused();
                bPaused = true;
                StopToolStripButton.Enabled = false;
                stopToolStripMenuItem.Enabled = false;
                PauseToolStripButton.Text = "Continue";
                pauseToolStripMenuItem.Text = "Continue";
            }
        }

        private void LoadConfigToolStripButton_Click(Object sender, EventArgs e)
        {
            if (m_XController.LoadConfig())
            {
                MessageBox.Show( "Config File has been loaded successfully.",
                                 "Information",
                                 MessageBoxButtons.OK,
                                 MessageBoxIcon.Information
                               );
                m_ProcessorsForm.CreateChainTree();
                m_SettingsForm.SetStuffs();

                SetSourceLinkComboBox();
                XTrackBar.Value = m_CrawlController.ShouldRunning;
            }
            else
            {
                MessageBox.Show( "Can not load the config file.",
                                 "Error",
                                 MessageBoxButtons.OK,
                                 MessageBoxIcon.Error
                               );
            }
        }

        private void SaveConfigToolStripButton_Click(Object sender, EventArgs e)
        {
            String[] urls;

            if ( !GetUrlsFromComboBox(out urls) )
            {
                if (XSourceLinkComboBox.Text != s_CombBoxTooltips)
                {
                    MessageBox.Show("Can not save to config file.",
                                     "Error",
                                     MessageBoxButtons.OK,
                                     MessageBoxIcon.Error
                                   );
                    return;
                }
            }
            
            m_XController.SetStuffsConfig(urls);

            if (!m_XController.SaveConfig())
            {
                MessageBox.Show( "Can not save to config file.",
                                 "Error",
                                 MessageBoxButtons.OK,
                                 MessageBoxIcon.Error
                               );
            }
            else
            {
                MessageBox.Show( "Config File has been saved successfully.",
                                 "Information",
                                 MessageBoxButtons.OK,
                                 MessageBoxIcon.Information
                               );
            }
        }

        private void runToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            GoButton_Click(sender, e);
        }

        private void stopToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            StopToolStripButton_Click(sender, e);
        }

        private void pauseToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            PauseToolStripButton_Click(sender, e);
        }

        private void saveToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            SaveConfigToolStripButton_Click(sender, e);
        }

        private void reLaodToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            LoadConfigToolStripButton_Click(sender, e);
        }

        private void exitToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            this.Close();
        }

        private void xSettingsToolStripMenuItem_CheckedChanged(Object sender, EventArgs e)
        {
            if (xSettingsToolStripMenuItem.Checked)
            {
                m_XDockManager.ShowContent(m_XSettingsFormContent);
            }
            else
            {
                m_XDockManager.HideContent(m_XSettingsFormContent);
            }
        }

        private void xProcessorToolStripMenuItem_CheckedChanged(Object sender, EventArgs e)
        {
            if (xProcessorToolStripMenuItem.Checked)
            {
                m_XDockManager.ShowContent(m_XProcessorsFormContent);
            }
            else
            {
                m_XDockManager.HideContent(m_XProcessorsFormContent);
            }
        }

        private void XDockManager_ContentHidden(Content c, EventArgs e)
        {
            if (c == m_XProcessorsFormContent &&
                xProcessorToolStripMenuItem.Checked
               )
                xProcessorToolStripMenuItem.Checked = false;
            else if (c == m_XSettingsFormContent &&
                     xSettingsToolStripMenuItem.Checked
                    )
                xSettingsToolStripMenuItem.Checked = false;
        }

        private void XDockManager_ContentShown(Content c, EventArgs e)
        {
            if (c == m_XProcessorsFormContent &&
                !xProcessorToolStripMenuItem.Checked
               )
                xProcessorToolStripMenuItem.Checked = true;
            else if (c == m_XSettingsFormContent &&
                     !xSettingsToolStripMenuItem.Checked
                    )
                xSettingsToolStripMenuItem.Checked = true;
        }

        private void aboutToolStripMenuItem_Click(Object sender, EventArgs e)
        {
            XAbout about = new XAbout();
            about.ShowDialog(this);
        }

        private System.Timers.Timer m_Timer;

        private Int32 ErrorSequence = 0;
        private Boolean bPaused = false;

        private DockingManager  m_XDockManager;
        private XController     m_XController;
        private CrawlController m_CrawlController;

        private XProcessorsForm m_ProcessorsForm;
        private XSettingsForm   m_SettingsForm;
        private XProgressForm   m_ProgressForm;

        private Content m_XProcessorsFormContent;
        private Content m_XSettingsFormContent;
    }
}